package com.itzxx.huazhun.utils

/**
 * Desc:
 *
 * Author: zhangxiaoxiao
 * Date: 2024/8/8 14:18
 * Updater:
 * Update Time:
 * Update Comments:
 */
object Snowflake {
    private const val EPOCH = 1577836800000L // 自定义起始时间戳（毫秒）
    private const val TWEPOCH = 1288834974657L // 自定义Twepoch（毫秒）
    private const val WORKER_ID_BITS = 5 // 机器ID所占的位数
    private const val DATACENTER_ID_BITS = 5 // 数据中心ID所占的位数
    private const val SEQUENCE_BITS = 12 // 序列号所占的位数

    private const val MAX_WORKER_ID = -1L xor (-1L shl WORKER_ID_BITS) // 最大机器ID
    private const val MAX_DATACENTER_ID = -1L xor (-1L shl DATACENTER_ID_BITS) // 最大数据中心ID
    private const val MAX_SEQUENCE = -1L xor (-1L shl SEQUENCE_BITS) // 最大序列号

    private var workerId: Long = 1 // 初始化机器ID
    private var datacenterId: Long = 1 // 初始化数据中心ID
    private var sequence: Long = 0L // 初始化序列号
    private var lastTimestamp = -1L // 上次生成ID的时间戳

    // 初始化Snowflake算法
    fun init(workerId: Long, datacenterId: Long) {
        if (workerId > MAX_WORKER_ID || workerId < 0) {
            throw IllegalArgumentException("workerId is out of range")
        }
        if (datacenterId > MAX_DATACENTER_ID || datacenterId < 0) {
            throw IllegalArgumentException("datacenterId is out of range")
        }
        this.workerId = workerId
        this.datacenterId = datacenterId
    }

    // 生成下一个ID
    fun nextId(): Long {
        var timestamp = currentTimestamp()
        if (timestamp < lastTimestamp) {
            throw IllegalStateException("Clock moved backwards. Refusing to generate id")
        }

        if (lastTimestamp == timestamp) {
            sequence = (sequence + 1) and MAX_SEQUENCE
            if (sequence == 0L) {
                timestamp = tilNextMillis(lastTimestamp)
            }
        } else {
            sequence = 0L
        }

        lastTimestamp = timestamp

        return ((timestamp - EPOCH) shl 22) or (datacenterId shl 17) or (workerId shl 12) or sequence
    }

    private fun currentTimestamp(): Long {
        return System.currentTimeMillis()
    }

    private fun tilNextMillis(lastTimestamp: Long): Long {
        var timestamp = currentTimestamp()
        while (timestamp <= lastTimestamp) {
            timestamp = currentTimestamp()
        }
        return timestamp
    }
}